package org.vacoor.xqq.core.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Random;
import java.util.Stack;

/**
 * User: vacoor
 */
public class QQUtil {
    public static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();

    /**
     * 解析返回的16进制表示的字节数组
     * \x00\x00\x00\x00\x83\xec\xb6\x7c
     *
     * @param key
     * @return
     */
    public static byte[] parseKey(String key) {
        String[] split = key.replaceAll("^\\\\x", "").split("\\\\x");
        byte[] k = new byte[split.length];
        for (int i = 0; i < k.length; i++) {
            k[i] = (byte) (Integer.parseInt(split[i], 16));
        }
        return k;
    }

    /**
     * QQ 登录密码加密
     * 1. 对明文密码MD5 得到字节数组A
     * 2. 将 A 连接上check时得到的key字节数组 得到字节数组B
     * 3. 对 B MD5 得到字节数组C
     * 4. 将 C 转换为HEX, 连接上验证码字符串得到 D
     * 5. 对 D MD5得到字节数组 E
     * 6. 将 E 转换为 HEX
     *
     * @param plain  明文密码
     * @param key    check时得到的key
     * @param verify 验证码
     * @return
     */
    public static String encrypt(String plain, byte[] key, String verify) {
        byte[] plainMD5 = md5(plain.getBytes());
        byte[] plainKeyMD5 = new byte[plainMD5.length + key.length];

        System.arraycopy(plainMD5, 0, plainKeyMD5, 0, plainMD5.length);
        System.arraycopy(key, 0, plainKeyMD5, plainMD5.length, key.length);

        byte[] result = (bytes2Hex(md5(plainKeyMD5)) + verify.toUpperCase()).getBytes();
        return bytes2Hex(md5(result));
    }

    public static byte[] md5(byte[] source) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            return digest.digest(source);
        } catch (NoSuchAlgorithmException e) {
        }
        return null;
    }

    public static String bytes2Hex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
//            sb.append(HEX_CHARS[(b & 0xF0) >>> 4]).append(HEX_CHARS[b & 0xF]);
            sb.append(HEX_CHARS[b >> 4 & 0xF]).append(HEX_CHARS[b & 0xF]);
        }
        return sb.toString();
    }

    /**
     * 获取好友列表时HASH值的算法, 其实就是排序中进行运算
     *
     * @param uin     channel 时得到的uin, 可能和QQ号相同, 如果邮箱登录,则chanel会返回相应uin
     * @param ptwebqq
     * @return
     */
    public static String hash(long uin, String ptwebqq) {
        Stack<IdxRange> stack = new Stack<IdxRange>();
        byte[] bytes = new byte[4];
        bytes[0] = (byte) (uin >> 24 & 0xFF);
        bytes[1] = (byte) (uin >> 16 & 0xFF);
        bytes[2] = (byte) (uin >> 8 & 0xFF);
        bytes[3] = (byte) (uin & 0xFF);

        // 本来应该是对 ASCII 排序, Java中char/int 通用, 就不转换了
        char[] array = ptwebqq.toCharArray();

        // 利用栈递归调用
        for (stack.push(new IdxRange(0, array.length - 1)); stack.size() > 0; ) {
            IdxRange range = stack.pop();
            int sIdx = range.s;
            int eIdx = range.e;

            if (sIdx >= eIdx || sIdx < 0 || eIdx >= array.length) {
                continue;
            }

            // 相邻的直接判断是否需要交换
            if (sIdx + 1 == eIdx) {
                // 如果前面值比后面大交换
                if (array[sIdx] > array[eIdx]) {
                    array[sIdx] ^= array[eIdx];
                    array[eIdx] ^= array[sIdx];
                    array[sIdx] ^= array[eIdx];
                }
            } else {
                // 查找
                int oSIdx = sIdx;

                // 记录上次查找的最后索引
                // 使用起始位置值, 作为标准值
                int X = array[sIdx];
                int lEIdx = eIdx;
                for (lEIdx = eIdx, X = array[sIdx]; sIdx < eIdx; ) {
                    // 在区间内从后向前找, 直到找到比标准值小的
                    for (; sIdx < eIdx & array[eIdx] >= X; ) {
                        eIdx--;
                        bytes[0] = (byte) (bytes[0] + 3 & 0xFF);
                        //
                    }

                    // 如果在区间内找到比标准值小的, 则将值放入标准值位置
                    if (sIdx < eIdx) {
                        array[sIdx] = array[eIdx];
                        // 起始索引后移
                        sIdx++;
                        bytes[1] = (byte) (bytes[1] * 13 + 43 & 0xFF);
                    }

                    // 在区间内找比标准值大的
                    for (; sIdx < eIdx && array[sIdx] <= X; ) {
                        sIdx++;
                        //
                        bytes[2] = (byte) (bytes[2] - 3 & 0xFF);
                    }

                    // 如果在区间内找到比标准值大的
                    if (sIdx < eIdx) {
                        // 赋值给刚刚找到的最大值位置
                        array[eIdx] = array[sIdx];
                        // 结束索引前移
                        eIdx--;
                        bytes[3] = (byte) ((bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3] + 1) & 0xFF);
                        //
                    }
                }
                array[sIdx] = (char) X;

                // 压栈递归
                stack.push(new IdxRange(oSIdx, sIdx - 1));
                stack.push(new IdxRange(sIdx + 1, lEIdx));
            }
        }
        return QQUtil.bytes2Hex(bytes);
    }

    /**
     * 计算好友列表Hash辅助类
     */
    private static class IdxRange {
        int s;
        int e;

        public IdxRange(int s, int e) {
            this.s = s < 0 ? 0 : s;
            this.e = e < 0 ? 0 : e;
        }
    }

    /**
     * 生成客户端ID
     * 100以内随机数 + 时间低六位
     *
     * @return
     */
    public static long genClientID() {
        return new Random().nextInt(100) * Math.round(1E6) + Math.round(new Date().getTime() % 1E6);
    }
}
